package com.huitone.smspfm.socket.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.bin.common.utils.StringUtils;
import com.huitone.smspfm.socket.core.handler.AbstractMessageHandler;
import com.huitone.smspfm.socket.core.net.ExtServerSocket;
import com.huitone.smspfm.socket.core.net.ExtSocket;
import com.huitone.smspfm.socket.core.net.ExtSocketManager;
import com.huitone.smspfm.socket.core.net.ExtSocketManager.AddResult;
import com.huitone.smspfm.socket.core.net.MessageHandlerProxy;
import com.huitone.smspfm.socket.core.verificate.Result;
import com.huitone.smspfm.socket.core.verificate.Validator;
import com.huitone.smspfm.socket.model.AccountInfo;
import com.huitone.smspfm.socket.protocol.sgip.constant.CommandID;
import com.huitone.smspfm.socket.protocol.sgip.constant.ErrorCode;
import com.huitone.smspfm.socket.protocol.sgip.message.Message;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind;
import com.huitone.smspfm.socket.protocol.sgip.message.msghead.Head;

import net.sf.cglib.proxy.Enhancer;

/**   
* @Title SmsServer.java 
* @Package com.huitone.smspfm.interfacz.server 
* @Description 接口服务 
* @author ybliang   
* @date 2017年10月10日 下午6:23:40 
* @version V1.0   
*/
public class SmsServer extends Thread {
	
	private static final Logger logger = LogManager.getLogger(SmsServer.class);
	
	private boolean keepGoing = true;
	
	private ExtServerSocket serverSocket = null;
	
	private int port = 8901;
	
	private Validator validator = null;
	
	private ExtSocketManager socketManager = null;
	
	/**
	 * 消息处理类
	 */
	private Class<? extends AbstractMessageHandler> messageHandlerClass = null;
	
	/**
	 * 消息处理类实例
	 */
	private AbstractMessageHandler messageHandler = null;
	
	/**
	 * Handler是否自动回复(重写后的方法还是需要显示向客户端返回响应)
	 */
	private boolean autoResponse = false;
	
	/**
	 * 需要拦截的IP黑名单
	 */
	private Set<String> blackIps = null;
	
	/**
	 * 需要忽略的IP
	 */
	private Set<String> ignoreIps = null;
	
	private ExecutorService executorService = new ThreadPoolExecutor(1000, 1000, 1L, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(1000 * 2), new RejectedExecutionHandler() {
		
		@Override
		public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
			boolean offer = false;
			while(!executor.isShutdown() && !offer) {
				offer = executor.getQueue().offer(r);
				if (!offer) {
					try {
						TimeUnit.MILLISECONDS.sleep(10);
					} catch (InterruptedException e) {}
				}
			}
		}
	});

	/**
	 * 开启服务
	 */
	public void startService() {
		super.start();
	}
	
	/**
	 * 停止服务
	 */
	public void stopService() {
		keepGoing = false;
		try {
			if (serverSocket != null && !serverSocket.isClosed()) {
				serverSocket.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (messageHandler != null) {
			messageHandler.destroy();
		}
		if (executorService != null && !executorService.isShutdown()) {
			executorService.shutdown();
		}
		if (socketManager != null) {
			socketManager.destory();
		}
	}

	@Override
	public void run() {
		
		logger.info("接口服务正在启动!");
		
		try {
			serverSocket = new ExtServerSocket(port);
			if (blackIps != null)
				serverSocket.setBlackIps(blackIps);
			if (ignoreIps != null) 
				serverSocket.setIgnoreIps(ignoreIps);
		} catch (IOException e) {
			logger.error("创建ServerSocket服务失败!");
		}
		
		if (serverSocket != null) {
			
			logger.info("接口服务已经启动!");
			
			while (keepGoing) {
				
				try {
					final ExtSocket accept = serverSocket.accept();
					
					executorService.execute(new Runnable() {
						
						@Override
						public void run() {
							
							try {
								ExtSocket socket = validate(accept);
								
								if (socket != null) {
									if (messageHandler != null) {
										socket.setHandler(messageHandler);
									} else {
										if (messageHandlerClass != null) {
											if (isAutoResponse()) {
												MessageHandlerProxy proxy = new MessageHandlerProxy();
												Enhancer enhancer = new Enhancer();
												enhancer.setSuperclass(messageHandlerClass);
												enhancer.setCallback(proxy);
												messageHandler = (AbstractMessageHandler) enhancer.create();
											} else {
												try {
													messageHandler = messageHandlerClass.newInstance();
												} catch (InstantiationException e) {
													e.printStackTrace();
												} catch (IllegalAccessException e) {
													e.printStackTrace();
												}
											}
											socket.setHandler(messageHandler);
										}
									}
									socket.setReceiveBufferSize(128 * 1024);
									socket.setSendBufferSize(128 * 1024);
									socket.setReadCyclic(true);
									socket.setKeepAlive(true);
									socket.setSoTimeout(0);
								}
							} catch (IOException e) {
								
								String ip = "";
								
								if (accept != null) {
									InetAddress inetAddress = accept.getInetAddress();
									if (inetAddress != null) {
										ip = inetAddress.getHostAddress();
									}
								}
								
								if (e instanceof SocketTimeoutException) {
									logger.warn("Socket校验连接合法性信息时,5s内没有读取到Bind消息,关闭连接!" + (!StringUtils.isEmpty(ip) ? ("IP:[" + ip + "]") : ""));
								} else if (e instanceof SocketException) {
									logger.warn("Socket校验连接合法性信息时出错,关闭连接!" + (!StringUtils.isEmpty(ip) ? ("IP:[" + ip + "]") : ""));
								} else {
									logger.error("Socket校验连接合法性信息时出错,关闭连接!" + (!StringUtils.isEmpty(ip) ? ("IP:[" + ip + "]") : ""), e);
								}
								
								if (accept != null && !accept.isClosed()) {
									try {
										accept.close();
									} catch (IOException e1) {
										logger.error("", e);
									}
								}
							}
						}
					});
				} catch (IOException e) {
					if (e instanceof SocketException) {
						if (serverSocket.isClosed()) {
							logger.info("ServerSocket关闭!");
						} else {
							if (!StringUtils.equals("Socket disconnected", e.getMessage())) {
								logger.error(e.getMessage(), e);
							}
						}
					} else {
						logger.error(e.getMessage(), e);
					}
				}
			}
			
			logger.info("接口服务正在停止!");
			
			if (!serverSocket.isClosed()) {
				try {
					serverSocket.close();
				} catch (IOException e) {}
			}
		}
		
		logger.info("接口服务已经停止!");
	}
	
	public ExtSocket validate(ExtSocket socket) throws IOException {
		
		if (socket != null && !socket.isClosed()) {
			
			if (validator != null) {
	        	
	        	socket.setSoTimeout(5000);
	        	
	        	Message message = socket.read();
	        	
				Head head = (Head) message.getHead();
				
				if (head.getCommandID() == CommandID.SGIP_BIND) {
					
					// 这是一条Bind请求
					Bind bind = (Bind) message.getBody();
					
					Result result = validator.verificate(bind,socket.getInetAddress().getHostAddress());
					
					if (result != null) {
						if (result.isAccept()) {
							
							AccountInfo accountInfo = result.getAccountInfo();
							if (accountInfo == null) {
								accountInfo = new AccountInfo();
								accountInfo.setSrvNode(bind.getSrvNode());
								accountInfo.setLoginName(bind.getLoginName());
								accountInfo.setLoginPassword(bind.getLoginPassword());
								accountInfo.setLogin(true);
							}
							
							accountInfo.setInetAddress(socket.getInetAddress());
							
							socket.setAccountInfo(accountInfo);
							
							if (socketManager != null) {
								
				        		AddResult added = socketManager.add(socket);
				        		
				        		switch (added) {
								case AddSucc:
									logger.info("绑定成功"+accountInfo.toString());
									socket.response(message, result.getErrorCode());
				        			return socket;
								case AddFail:
									logger.info("绑定失败,添加不成功" + accountInfo.toString());
									socket.response(message, ErrorCode.CODE_15);
									break;
									
								case MapNull:
									logger.info("绑定失败,添加不成功" + accountInfo.toString());
									socket.response(message, ErrorCode.CODE_15);
									break;
									
								case MaxPerSize:
									Integer integer = socketManager.getPersktsize().get(socket.getAccountInfo().toString());
									logger.warn("绑定失败,同一客户端套接字列表已满,最大可接受套接字连接数:" + ((integer == null) ? 0 : integer) +"," + accountInfo.toString());
									socket.response(message, ErrorCode.CODE_13);
									break;
									
								case MaxSize:
									logger.warn("绑定失败,套接字管理器已满,最大可接受套接字连接数:" + socketManager.getMaxSize() +"," + accountInfo.toString());
									socket.response(message, ErrorCode.CODE_12);
									break;
								}
				        		
				        		socket.close(); return null;
				        		
				        	} else {
				        		socket.response(message, result.getErrorCode()); return socket;
				        	}
						} else {
							socket.response(message, result.getErrorCode()); socket.close(); return null;
						}
					} else {
						throw new NullPointerException("Result is null");
					}
				} else {
					socket.response(message, ErrorCode.CODE_16);
					socket.close();
	            	return null;
				}
	        } else {
	        	if (socketManager != null) {
	        		socket.setAccountInfo(new AccountInfo());
	        		AddResult added = socketManager.add(socket);
	        		if (added == AddResult.AddSucc) {
	        			return socket;
	        		} else {
	        			return null;
	        		}
	        	} else {
	        		return socket;
	        	}
	        }
		} else {
			return null;
		}
	}

	/**
	 * 该方法已经弃用,请使用startService()
	 */
	@Deprecated
	@Override
	public synchronized void start() {
		throw new UnsupportedOperationException("Please Use startService() instead of start!");
	}

	/**
	 * 服务是否还在运行
	 * @return
	 */
	public boolean isKeepGoing() {
		return keepGoing;
	}

	/**
	 * 获取服务端口号
	 * @return
	 */
	public int getPort() {
		return port;
	}

	/**
	 * 设置服务端口号
	 * @param port
	 * @return
	 */
	public SmsServer setPort(int port) {
		this.port = port;
		return this;
	}

	/**
	 * 获取服务的用户合法性校验器
	 * @return
	 */
	public Validator getValidator() {
		return validator;
	}

	/**
	 * 设置用户合法性校验器
	 * @param validator
	 * @return
	 */
	public SmsServer setValidator(Validator validator) {
		this.validator = validator;
		return this;
	}

	/**
	 * 获取套接字连接管理器
	 * @return
	 */
	public ExtSocketManager getSocketManager() {
		return socketManager;
	}

	/**
	 * 设置套接字连接管理器
	 * @param socketManager
	 * @return
	 */
	public SmsServer setSocketManager(ExtSocketManager socketManager) {
		this.socketManager = socketManager;
		return this;
	}

	/**
	 * 获取信息处理器
	 * @return
	 */
	public AbstractMessageHandler getMessageHandler() {
		return messageHandler;
	}

	/**
	 * 设置信息处理器
	 * @param messageHandler
	 * @return
	 */
	public SmsServer setMessageHandler(AbstractMessageHandler messageHandler) {
		this.messageHandler = messageHandler;
		return this;
	}
	
	/**
	 * 设置黑名单IP
	 * @param ips
	 * @return
	 */
	public SmsServer setBlackIps(Set<String> ips) {
		
		this.blackIps = ips;
		
		if (serverSocket != null) {
			serverSocket.setBlackIps(ips);
		}
		
		return this;
	}
	
	/**
	 * 设置被忽略的IP
	 * @param ips
	 * @return
	 */
	public SmsServer setIgnoreIps(Set<String> ips) {
		
		this.ignoreIps = ips;
		
		if (serverSocket != null) {
			serverSocket.setIgnoreIps(ips);;
		}
		
		return this;
	}

	public Set<String> getBlackIps() {
		return blackIps;
	}

	public Set<String> getIgnoreIps() {
		return ignoreIps;
	}

	public Class<? extends AbstractMessageHandler> getMessageHandlerClass() {
		return messageHandlerClass;
	}

	public SmsServer setMessageHandlerClass(Class<? extends AbstractMessageHandler> messageHandlerClass) {
		this.messageHandlerClass = messageHandlerClass;
		return this;
	}

	public boolean isAutoResponse() {
		return autoResponse;
	}

	public SmsServer setAutoResponse(boolean autoResponse) {
		this.autoResponse = autoResponse;
		return this;
	}
}
